/*
 * Copyright (c) 2011 Sapienza University of Rome.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the
 *   distribution.
 * - Neither the name of the Sapienza University of Rome nor the names of
 *   its contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL SAPIENZA
 * UNIVERSITY OF ROME OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @author Ugo Colesanti <colesanti@dis.uniroma1.it>
 * @version 1.01 (October 10, 2011)
 *
 */

#warning "********* DISSense Mode - SINK *************"

module SinkApplicationC{
	uses {
		// Interfaces for initialization:
		interface Boot;
		interface SplitControl as RadioControl;
		interface StdControl as ManagerControl ;

		interface Receive;
		interface Send ; // should be removed

		interface Leds;

		interface CollectionPacket ;
		interface CtpPacket ;

		interface Timer<TMilli> as Timer ;
		interface Queue<packet_received_t> as PacketReceivedQueue ;

		interface FlashLog ;

		interface SplitControl as SerialControl ;
		interface AMSend as SerialSend;

		interface DISSenseSampling ;
		interface DISSenseStatistics ;

	}

}

implementation {

	task void startApplication() ;
	task void uartSendTask() ;

	uint8_t uartlen ;
	message_t uartbuf ;
	bool uartbusy = FALSE ;

	event void Boot.booted(){
//		call FlashLog.erase() ;
		if (call RadioControl.start() != SUCCESS) {
			// do nothing
		}
	}

	event void RadioControl.startDone(error_t error){
		if (error != SUCCESS){
			return ; // do nothing
		}

		if(call SerialControl.start() != SUCCESS){
			post startApplication() ;
		}
	}


	event void SerialControl.startDone(error_t error){
		post startApplication() ;
	}

	event void SerialControl.stopDone(error_t error){}


	task void startApplication(){
		dbg("Application","Application started\n") ;
		call ManagerControl.start() ;

		// DISSense started, now for data ...
	}


	event void Timer.fired(){
		if(call PacketReceivedQueue.size() > 0){
			atomic{
				//				if(!lm_busy){
				//			packet_received_t pktrx = call PacketReceivedQueue.head() ;
				//			log_buf = call PacketReceivedQueue.head() ;
				//			if(call LogWrite.append(&log_buf,sizeof(packet_received_t)) != SUCCESS){
				//				lm_busy = FALSE ;
				//				call Timer.startOneShot(512) ;
				//			}
				//			printf("Packet from: %u, parent: %u, seqno: %u, sensor: %u, retries: %u, oos: %u, thl: %u, queue: %u, failed: %u, sp: %u, gt: %u, ri: %u, dci: %u, sk: %u - Tchk: %lu\n",pktrx.from, pktrx.parent, pktrx.seqno, pktrx.sensor, pktrx.retries, pktrx.oos_cnt,pktrx.thl,pktrx.inQueue,pktrx.failed,pktrx.samplingPeriod,pktrx.gt,pktrx.ri,pktrx.dci,pktrx.skip,pktrx.timestamp) ; // was enabled in august

				packet_received_t pktrx = call PacketReceivedQueue.head() ;
				//			app_data_t* serialmsg = (app_data_t*) call SerialSend.getPayload(&uartbuf, sizeof(app_data_t));
				packet_received_t* serialmsg = (packet_received_t*) call SerialSend.getPayload(&uartbuf, sizeof(packet_received_t));
				app_data_t* serial_app_data = &serialmsg->app_data ;
				stats_t* serial_stats = &serialmsg->stats ;
				stats_schedule_t* serial_stats_schedule = &serialmsg->stats_schedule ;
				app_data_t* app_data = &pktrx.app_data ;
				uint8_t datareadings ;

				dbg("DEBUG_TTRD","Packet from: %u, parent: %u, temp: %u, hum: %u, thl: %u - Tchk: %lu\n",pktrx.from, pktrx.parent, pktrx.seqno, pktrx.oos_cnt,pktrx.thl,pktrx.timestamp) ;
#ifndef TOSSIM
				//			printf("Packet from: %u, parent: %u, seqno: %u, sensor: %u, retries: %u, oos: %u, thl: %u, queue: %u, failed: %u, sp: %u, gt: %u, ri: %u, dci: %u, sk: %u, Trx: %lu - Tchk: %lu\n",pktrx.stats.from, pktrx.app_data.sensor[0].data, pktrx.app_data.seqno, pktrx.app_data.voltage, pktrx.app_data.sensor[0].temp, pktrx.app_data.sensor[2].data,pktrx.stats.thl,pktrx.app_data.sensor[1].data,pktrx.app_data.sensor[1].temp,pktrx.app_data.sensor[2].temp,pktrx.app_data.sensor[3].data,pktrx.app_data.sensor[3].temp,pktrx.app_data.sensor[4].data,pktrx.app_data.sensor[4].temp,pktrx.app_data.timestamp,pktrx.stats.rxTimestamp) ; // was enabled in august
				//			printfflush() ; // was enabled in august


				if(serialmsg == NULL){

				}
				else{
					serial_stats_schedule->skip = call DISSenseStatistics.getSkip() ;
					serial_stats_schedule->gt = call DISSenseStatistics.getGt() ;
					serial_stats_schedule->ri = call DISSenseStatistics.getRi() ;
					serial_stats_schedule->dci = call DISSenseStatistics.getDci() ;

					serial_stats->from = pktrx.stats.from ;
					serial_stats->thl =  pktrx.stats.thl ;
					serial_stats->rxTimestamp = pktrx.stats.rxTimestamp ;

					serial_app_data->voltage = app_data->voltage ;
					serial_app_data->seqno = app_data->seqno ;
					serial_app_data->timestamp = app_data->timestamp ;
					serial_app_data->sensor_timer = app_data->sensor_timer ;

					for(datareadings = 0 ; datareadings < SENSOR_COUNT ; datareadings++){
						serial_app_data->sensor[datareadings].data = app_data->sensor[datareadings].data ;
						serial_app_data->sensor[datareadings].temp = app_data->sensor[datareadings].temp ;
					}

					if(call FlashLog.log(serialmsg,sizeof(packet_received_t)) != SUCCESS){
						uartlen = sizeof(packet_received_t);
						post uartSendTask();
					}


				}
#endif

				//			call PacketReceivedQueue.dequeue() ;
				//			call Timer.startOneShot(512) ;
			}
			//			}
		}
	}

	event message_t*
	Receive.receive(message_t* msg, void *payload, uint8_t len) {
		if(len == sizeof(app_data_t)){
			atomic{
				packet_received_t pktrx ;
				app_data_t* data = &pktrx.app_data ;
				uint8_t datareadings ;
				app_data_t* app_data = (app_data_t*) payload ;

				//				memcpy(data,app_data,sizeof(app_data_t)) ;
				data->voltage = app_data->voltage ;
				data->seqno = app_data->seqno ;
				data->timestamp = app_data->timestamp ;
				data->sensor_timer = app_data->sensor_timer ;

				for(datareadings = 0 ; datareadings < SENSOR_COUNT ; datareadings++){
					data->sensor[datareadings].data = app_data->sensor[datareadings].data ;
					data->sensor[datareadings].temp = app_data->sensor[datareadings].temp ;
				}

				pktrx.stats.from = call CollectionPacket.getOrigin(msg) ;
				pktrx.stats.thl = call CtpPacket.getThl(msg) ;
				pktrx.stats.rxTimestamp = call Timer.getNow() ;

				call Timer.startOneShot(10240) ; // just to not interfere with other printf ...
				call PacketReceivedQueue.enqueue(pktrx) ;

			}
		}
		return msg;
	}

	event void RadioControl.stopDone(error_t error){}


	event void FlashLog.logDone(void* msg, error_t result){
//		post sendPacket() ;
//		if(result == SUCCESS){
			uartlen = sizeof(packet_received_t);
			post uartSendTask();
//		}
//		else{
//			call FlashLog.log(msg,sizeof(packet_received_t)) ;
//			call Leds.led2Toggle() ;
//		}

	}

	event void FlashLog.eraseDone(error_t error){
		if(error == SUCCESS) call Leds.led2On() ;
	}

	task void uartSendTask() {
		if (call SerialSend.send(0xffff, &uartbuf, uartlen) != SUCCESS) {
			call PacketReceivedQueue.dequeue() ;
			call Timer.startOneShot(512) ;
		} else {
			uartbusy = TRUE;
		}
	}

	event void SerialSend.sendDone(message_t *msg, error_t error) {
		uartbusy = FALSE;
		call PacketReceivedQueue.dequeue() ;
		call Timer.startOneShot(512) ;
	}

	event void Send.sendDone(message_t* msg, error_t error) {} // should be removed

	event void DISSenseSampling.startSampling() {}

	event void DISSenseSampling.syncNotification(bool inSync){}
}
